﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace MessageServer.utilities
{
    internal class ListObject<T>
    {
        internal T _object;
        internal ListObject<T> _next;
    }

    /**
     * This is a thread safe list, the push and pop occurs without locking using the interlock exchange
     * that exchanges data in an atomic instruction.
     * Once added an object has to be popped out. Providing a central removal will beat the purpose
     * of this class. So, don't use it, push and pop is all that is possible.
     * This is a circularly linked list so that both the push and the pop use the same
     * object. 
     * */
    public class TSList<T>
    {
        ListObject<T> _head = null;
        ListObject<T> _tail = null;

        //having problems making this a lock free implementation. For now it is not.
        //TODO: will come back and visit it if it becomes a synch problem
        //object acting = new object();

        /**
         * Has to be initialized with atleast a single object. This is created
         * in only a single thread and hence no contention. This object will never be 
         * retrieved, so it is just a buffer.
         **/
        public TSList(T obj)
        {
            _head = create(obj);
            _tail = _head;
            _head._next = _head; //create it as a circular list.
        }

        private ListObject<T> create(T obj)
        {
            ListObject<T> ret = new ListObject<T>();
            ret._object = obj;
            ret._next = _head; //circular list

            return ret;
        }


        /**
         * When pushing push at the tail always.
         * 
         * @param obj the object to push
         **/
        public void push(T obj)
        {
            ListObject<T> thisobj = create(obj);
            if ((_head == null) || (_tail == null))
            {
                //shd not happen, if it does a problem.
                throw new Exception("Head null. Should not happen.");
            }
            else
            {
                thisobj._next = _head._next;
                while (thisobj._next != Interlocked.CompareExchange(ref _head._next, thisobj, thisobj._next))
                {
                    //if it is not the same, then another thread has changed, so reset and try
                    thisobj._next = _head._next;
                }

                //lock (acting)
                //{
                //    _tail._next = thisobj;
                //    _tail = thisobj;
                //    UtilityLogger.LOG.LogDebug("TSList: Pushed object " + obj + "object is: " + _head._next);
                //}
            }
        }

        public T pop()
        {
            //lock (acting)
            //{
            //    ListObject<T> val = _head._next;
            //    _head._next = val._next;
            //    if (_head._next == _head)
            //        _tail = _head;

            //    return val._object;
            //}
            ListObject<T> val = _head._next;
            if (val == _head)
            {
                return _head._object; //return the dummy. don't care abt aything else
            }

            //try to get the object from the head. 
            while ((val != _head) &&
                (val != Interlocked.CompareExchange(ref _head._next, val._next, val)))
            {
                val = _head._next;
            }

            return val._object;

            /*if (val == _head)
            {
                //need to set the tail correctly here.
                val = _tail;
                ListObject<T> exchanged = Interlocked.Exchange(ref _tail, _head);
                UtilityLogger.LOG.LogDebug("TSList: Popped dummy object ");
                return _head._object; //return the dummy for comparison
            }
            else
            {
                UtilityLogger.LOG.LogDebug("TSList: Popped object ");
                return val._object;
            }*/
        }

        public bool empty { get { return (_head._next == _head); } } //if only dummy element present it is empty
    }
}
